﻿/*
 * protocolbase.hpp
 *
 *  Created on: 2017年4月13日
 *      Author: work
 */

#ifndef _DM_PROTOCOL_PROTOCOLBASE_HPP_
#define _DM_PROTOCOL_PROTOCOLBASE_HPP_

#include <dm/export.hpp>

#ifndef DM_API_PROTOCOL
#define DM_API_PROTOCOL DM_API_IMPORT
#endif

#include <dm/protocol/frame.hpp>
#include <dm/runtimestamp.hpp>
#include <dm/counter.hpp>

namespace dm{
namespace protocol{

/**
 * 通信规约基类
 */
class DM_API_PROTOCOL CProtocolBase{
public:
	typedef dm::protocol::CFrame frame_t;

	/**
	 * 刷新延时间隔。真实时长由实现确定
	 */
	typedef dm::uint64 interval_t;

	/**
	 * 流量计数器类型
	 */
	typedef dm::TCounter<dm::uint32> count_t;

	/**
	 * 时钟类型
	 */
	typedef dm::CTimeStamp ts_t;

	/**
	 * 运行时钟类型
	 */
	typedef dm::CRunTimeStamp rts_t;

	/**
	 * 通信处理动作
	 */
	enum EAction{
		Action_Nothing = 0,			//!< 不需要动作。不需要发送数据时一般使用
		Action_Send,						//!< 发送数据
		Action_ReTime,					//!< 下次刷新重新计时
		Action_SendAndReTime,	//!< 发送并下次刷新重新计时。有数据需要发送时，一般使用
		Action_ResetBuf,				    //!< 复位buf缓冲区
		Action_CloseLink,					//!< 关闭链路
		Action_Reset
	};

	CProtocolBase();
	virtual ~CProtocolBase();

	/**
	 * 规约名称
	 * 必须在子类中有实现
	 * @return
	 */
	virtual const char* name()const=0;

	/**
	 * 获取接收帧
	 * 每次解析时，都通过本函数获取
	 * @return
	 */
	virtual CFrame& getRxFrame()=0;

	/**
	 * 获取发送帧
	 * 每次发送时，都通过本函数获取
	 * @return
	 */
	virtual const CFrame& getTxFrame()=0;

	/**
	 * 连接成功时处理函数
	 * @param now
	 */
	virtual void onLinkSettled( const ts_t& ts,const rts_t& rts );

	/**
	 * 连接关闭是处理函数
	 * @param now
	 */
	virtual void onLinkClosed( const ts_t& ts,const rts_t& rts );

	/**
	 * 对接收到的报文进行过滤处理
	 * @return 是否过滤
	 * - true 过滤
	 * - false 不过滤
	 */
	virtual bool filterRxFrame()const;

	/**
	 * 收到新报文时，会调用该函数处理之
	 * @return
	 */
	virtual EAction dealRxFrame( const ts_t& ts,const rts_t& rts )=0;

	/**
	 * 定时处理任务
	 * @param txFrame
	 * @return
	 */
	virtual EAction timedRefresh( const ts_t& ts,const rts_t& rts );

	/**
	 * 复位处理
	 * @param now
	 */
	virtual void reset( const ts_t& ts,const rts_t& rts );

	/**
	 * 设置更新间隔
	 * @param interval
	 * - 0 不延时
	 */
	inline void setRefreshInterval( const interval_t& interval ){
		m_refreshInterval = interval;
	}

	/**
	 * 获取下次更新延时
	 * @return
	 */
	inline const interval_t& getRefreshInterval()const{
		return m_refreshInterval;
	}

	/**
	 * 获取最后发送报文时刻
	 * @return
	 */
	inline const rts_t& getLastSendTime()const{
		return m_lastSendTime;
	}

	/**
	 * 获取最后接收报文时刻
	 * @return
	 */
	inline const rts_t& getLastRecvTime()const{
		return m_lastRecvTime;
	}

	/**
	 * 更新接收统计信息
	 * @param bytes
	 * @param n
	 */
	virtual void updateRecvTime( dm::uint32 bytes,const rts_t& rts );

	/**
	 * 更新发送统计信息
	 * @param bytes
	 * @param n
	 */
	virtual void updateSendTime( dm::uint32 bytes,const rts_t& rts );

	/**
	 * 获取接收流量计数器
	 * @return
	 */
	inline const count_t& getRxCounter()const{
		return m_rxCnt;
	}

	/**
	 * 获取发送流量计数器
	 * @return
	 */
	inline const count_t& getTxCounter()const{
		return m_txCnt;
	}

	/**
	 * 复位接受流量计数器
	 * 一般不复位，除有特殊要求
	 */
	inline void resetRxCounter(){
		m_rxCnt.reset();
	}

	/**
	 * 复位发送流量计数器
	 * 一般不复位，除有特殊要求
	 */
	inline void resetTxCounter(){
		m_txCnt.reset();
	}

	/**
	 * 是否发送报文后没有接收到报文
	 * @return
	 */
	inline bool isSendBeforeRecv()const{
		return m_lastSendTime<m_lastRecvTime;
	}

	/**
	 * 是否接收报文后未发送
	 * @return
	 */
	inline bool isRecvBeforeSend()const{
		return m_lastSendTime>m_lastRecvTime;
	}

	/**
	 * 上次发送是否超时
	 * @param seconds
	 * @return
	 */
	inline bool isSendTimeout( const interval_t& seconds,const rts_t& rts )const{
		return m_lastSendTime.isTimeout_sec(seconds,rts);
	}

	/**
	 * 上次接收是否超时
	 * @param seconds
	 * @return
	 */
	inline bool isRecvTimeout( const interval_t& seconds,const rts_t& rts )const{
		return m_lastRecvTime.isTimeout_sec(seconds,rts);
	}

	/**
	 * 获取链路最后建立时刻
	 * @return
	 */
	inline const rts_t& getLastLinkSettled()const{
		return m_lastLinkStl;
	}

	/**
	 * 获取链路最后断开时刻
	 * @return
	 */
	inline const rts_t& getLastLinkDsct()const{
		return m_lastLinkDsct;
	}

	/**
	 * 当前是否等待
	 * @return
	 */
	inline bool ifWait()const{
		return !m_lastWait.isInv();
	}

	/**
	 * 等待是否已经超时
	 * @param now
	 * @return
	 */
	inline bool ifWaitTimeout( const rts_t& rts )const{
		return m_waitTimeout>0 && m_lastWait.isTimeout_sec(m_waitTimeout,rts);
	}

	/**
	 * 等待是否超过次数
	 * @return
	 */
	inline bool ifWaitOverTimes()const{
		return m_timeoutMaxTimes>0 && m_timeoutTimes>m_timeoutMaxTimes;
	}

	/**
	 * 设置等待状态
	 * @param w
	 */
	inline void wait( const rts_t& rts ){
		m_lastWait = rts;
		++m_timeoutTimes;
	}

	/**
	 * 取消等待状态
	 */
	inline void noWait(){
		m_lastWait.setInv();
		m_timeoutTimes = 0;
	}

	/**
	 * 设置等待超时时长
	 * @param secs 超时时长。0 永远不超时
	 */
	inline void setWaitTimeout( rts_t::sec_t secs ){
		m_waitTimeout = secs;
	}

	/**
	 * 设置最大等待次数
	 * @param t 等待次数。0 表示无限大
	 */
	inline void setWaitMaxTimes( dm::uint16 t ){
		m_timeoutMaxTimes = t;
	}

	/**
	 * 检测是否等待
	 * return
	 * - Action_Nothing 无等待
	 */
	virtual EAction checkWait( const rts_t& now );

protected:
	/**
	 * 刷新间隔
	 */
	interval_t m_refreshInterval;

	/**
	 * 最后发送报文时刻
	 */
	rts_t m_lastSendTime;

	/**
	 * 最后接收报文时刻
	 */
	rts_t m_lastRecvTime;

	/**
	 * 链路最后断开时刻
	 */
	rts_t m_lastLinkDsct;

	/**
	 * 链路最后连接时刻
	 */
	rts_t m_lastLinkStl;

	/**
	 * 最后等待开始时刻
	 */
	rts_t m_lastWait;

	// 超时管理的接口是提供给有需要的子类使用的
	// 基类并不对该接口进行处理

	/**
	 * 等待超时时长
	 * 0 表示永远不超时
	 */
	rts_t::sec_t m_waitTimeout;

	/**
	 * 等待超时次数
	 */
	dm::uint16 m_timeoutTimes;

	/**
	 * 最大等待超时次数
	 * 超过将断开连接
	 * 0 永远不判断最大超时次数
	 */
	dm::uint16 m_timeoutMaxTimes;

	/**
	 * 接收流量计数器。规约复位不清零
	 */
	count_t m_rxCnt;

	/**
	 * 发送流量计数器。规约复位不清零
	 */
	count_t m_txCnt;
};

}
}

#endif /* INCLUDE_DM_PROTOCOL_PROTOCOLBASE_HPP_ */
